﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using PhiStore.Policies;
using PhiStore.Threading;
using PhiStore.Util;

namespace PhiStore.ExampleCodes.PhiElection
{
    class ElectionResultGenerator : Boostable
    {
        private const string ElectionResultKey = "voting_result_2011";
        private const ulong TotalVotes = 5500000; //5.5 million voters
        private ulong VotesCounted = 0L;
        private static string[] parties = new string[] {"Socialdemokraterne (A)", 
                                                        "Radikale (B)",
                                                        "Konservative (C)",
                                                        "Socialistisk Folkeparti (F)",
                                                        "Dansk Folkeparti (O)",
                                                        "Venstre (V)",
                                                        "Liberal Alliance (I)",
                                                        "Enhedslisten (Ø)",
                                                        "Øvrige"};

        // a recent poll: http://net-demokrati.dk/Meningsmaaling.120.0.html
        private static double[] goalPercentages = new double[] { 27.1, 5.7, 7.3, 14.9, 13.1, 24.1, 4.1, 3.3, 0.4 };
        private int[] lookupArray = new int[1000];
        private void generateLookupArray()
        {
            int index = 0;
            for (int i = 0; i < parties.Length; i++)
            {
                int percentage = Convert.ToInt32(goalPercentages[i] * 10);
                while (percentage-- > 0)
                {
                    lookupArray[index++] = i;
                }
            }
        }

        /// <summary>
        /// generates a single vote
        /// </summary>
        /// <returns>the party which got a vote</returns>
        private string Vote()
        {
            int vote = RandomGenerator.Integer(lookupArray.Length);
            return parties[lookupArray[vote]];
        }

        PhiClient<ElectionResult> client;
        ElectionResult result;
        Dictionary<string, ulong> partyVotes;

        public ElectionResultGenerator(IPEndPoint server)
        {
            generateLookupArray();
            client = new PhiClient<ElectionResult>(false, false);
            client.Initialize();
            client.Connect(server.Address, server.Port);

            Policy policy = new Policy();
            policy.EnforceAtomicity = false;
            policy.FastReturn = true;
            policy.ReplicationNumber = 1;
            policy.MayGarbageCollect = true;
            policy.IdleThreshold = 365L * 24L * 60L * 60L * 1000L; // a year

            partyVotes = new Dictionary<string, ulong>();
            Dictionary<string, double> partyPercentage = new Dictionary<string, double>();
            foreach (string party in parties)
            {
                partyPercentage.Add(party, 0.0);
                partyVotes.Add(party, 0L);
            }

            result = new ElectionResult(partyPercentage, 0L, TotalVotes);
            client.Create(ElectionResultKey, result, policy);

            Booster.Boost(this);



        }

        private void updateVotingResult()
        {
            foreach (string party in parties)
            {
                result.PartyPercentage[party] = (Convert.ToDouble(partyVotes[party]) / Convert.ToDouble(VotesCounted)) * 100.0;
            }
            result.VotesCounted = VotesCounted;
            result.VotesRemaining = TotalVotes - VotesCounted;
            client.Update(ElectionResultKey, result);
        }

        public void Run()
        {
            while (VotesCounted++ < TotalVotes)
            {
                string partyGotVote = Vote();
                partyVotes[partyGotVote]++;

                if (VotesCounted % 100000 == 0)
                {
                    Thread.Sleep(1000); //keeping the public in suspense
                    try
                    {
                        updateVotingResult();
                    }
                    catch (Exception)
                    {
                        break;
                    }
                    
                }
                
                if(VotesCounted % 25 == 0) //it's too fast - slow down a bit
                    Thread.Sleep(1);
            }
            try
            {
                VotesCounted = TotalVotes;
                updateVotingResult();
            }
            catch (Exception)
            {
            }
            client.Disconnect();
        }
    }
}
